//数据存储的key
const keys = {
  user: 'user' //存储用户信息的key
};
//文件类型。参考：https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
const MIMETypes = {
  json: 'application/json',
  jpg: 'image/jpeg',
  png: 'image/png',
  xls: 'application/vnd.ms-excel', //windows的表格：旧版
  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', //windows的表格：新版
  csv: 'text/csv,charset=UTF-8' //mac的表格
};

//常用工具类
export default {
  /********************************处理数字********************************/

  /**
   * 获取浮点数
   * @param value 输入的值
   * @param length 小数点后长度
   * @param isFix 小数点后长度是否固定
   */
  getFloat({value, length = 2, isFix = false}) {
    let result = parseFloat(value);
    if (isNaN(result)) return '';

    if (isFix) {
      result = result.toFixed(length);
    } else {
      let level = Math.pow(10, length); //位数
      result = Math.round(result * level) / level;
    }
    return result;
  },

  /********************************处理object********************************/

  /**
   * 去除空格
   * @param data 需要处理的数据。类型：string|object。
   * @param type 类型：0表示去掉首尾空格，1表示去掉所有空格。
   */
  getTrim(data, type = 0) {
    if (!data) return data;

    let reg = '';
    if (type === 0) {
      reg = /(^\s*)|(\s*$)/g; //\s表示空白符，*表示0个或多个，^表示行首，$表示行尾
    } else if (type === 1) {
      reg = /\s*/g;
    }

    if (typeof data === 'string') {
      return data.replace(reg, '');
    } else if (typeof data === 'object') {
      for (let key in data) {
        let item = data[key];
        if (item && typeof item === 'string') {
          data[key] = item.replace(reg, '');
        }
      }
    }
    return data;
  },

  /**
   * 获取简化的数据
   * @param data 需要简化的数据
   * @param config 配置。类型：{removeBlank,isTrim}。
   *  removeBlank 是否删除空白字段。类型：Boolean。默认值：true。
   *  isTrim      是否对字段的值去掉前后空格。类型：Boolean。默认值：true。
   */
  getSimplify(data, config = {}) {
    if (typeof data !== 'object') return data;

    let {removeBlank = true, isTrim = true} = config;
    if (isTrim) data = this.getTrim(data);
    for (let key in data) {
      let item = data[key];
      if (item === undefined || item === null || (removeBlank && item === '')) {
        delete data[key];
      }
    }
    return data;
  },

  /**
   * 深层拷贝对象
   * @param data 需要拷贝的对象
   */
  copyObject(data) {
    let result = Array.isArray(data) ? [] : {};
    for (let key in data) {
      if (typeof data[key] === 'object' && data[key] !== null) {
        result[key] = this.copyObject(data[key]); //递归复制
      } else {
        result[key] = data[key];
      }
    }
    return result;
  },

  /********************************处理数组********************************/

  /**
   * 判断数组是否有重复项
   * @param arr 需要判断的数组
   * @param key 需要判断的键
   */
  isArrayRepeat(arr, key) {
    if (!arr || arr.length === 0) return false;
    if (key) arr = arr.map((item) => item[key]);

    //ES6的数据结构：成员值唯一的数组。
    let set = new Set(arr);
    return set.size !== arr.length;
  },

  /********************************处理document********************************/

  /**
   * 在document中添加script
   * @param url 需要添加的js地址
   * @param config 配置。类型：{charset}。
   */
  addScript(url, config = {}) {
    return new Promise(function (resolve, reject) {
      let {charset = 'utf-8'} = config;
      let script = document.createElement('script');
      script.charset = charset;
      script.src = url;

      script.onload = (r) => {
        resolve(r);
      };
      script.onerror = (e) => reject(e);

      document.head.appendChild(script);
    });
  },

  /**
   * 复制文本
   * @param data 文本或元素id
   * @param type 类型：0为文本，1为元素id
   */
  copyText(data, type = 0) {
    let e; //对应的元素
    if (type === 0) {
      e = document.createElement('textarea');
      e.value = data;
      document.body.appendChild(e);
    } else {
      e = document.getElementById(data);
    }
    e.select(); // 选择对象
    document.execCommand('Copy'); // 执行浏览器复制命令
    if (type === 0) {
      document.body.removeChild(e);
    }
  },

  /**
   * 获取React元素
   * @param dom dom元素
   */
  getViewReact(dom) {
    if (!dom) return;

    const key = Object.keys(dom).find((key) => key.startsWith('__reactInternalInstance'));
    const fiberNode = dom[key];
    if (!fiberNode) return;
    // react <16
    if (fiberNode._currentElement) {
      let compFiber = fiberNode._currentElement._owner;
      return compFiber._instance;
    }

    // react 16+
    if (fiberNode.return) {
      let parentFiber = fiberNode;
      //React元素位于Dom元素的上层：找到上层的第一个React元素：React元素的memoizedState不为null
      while (!parentFiber.memoizedState) {
        parentFiber = parentFiber.return;
      }
      return parentFiber.stateNode;
    }
  },

  /**
   * 获取Vue元素
   * @param dom dom元素
   */
  getViewVue(dom) {
    if (!dom) return;
    //获取Vue元素
    const vueView = dom.__vue__;
    return vueView;
  },

  /**********************处理文件**********************/
  /**
   * 获取文件类型：根据文件名
   * @param fileName 文件名
   * @param prefix 返回的文件类型是否包含前缀'.'
   */
  getFileType(fileName, prefix = false) {
    if (!fileName) return;
    let index = fileName.lastIndexOf('.');
    return fileName.substring(prefix ? index : index + 1);
  },
  /**
   * 获取文件数据：根据文件
   * 参考：https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
   * @param file 文件：文件上传后得到的File对象，格式为{uid,type,size,name,...}
   * @param type 文件数据格式：0是Base64，1是字符串，2是Buffer数组，默认为0
   */
  getDataFromFile(file, type = 0) {
    if (!file) return Promise.resolve();

    //文件读取工具。
    let reader = new FileReader();
    if (type === 0) {
      //读取Base64格式的文件数据
      reader.readAsDataURL(file);
    } else if (type === 1) {
      //读取字符串格式的文件数据
      reader.readAsText(file);
    } else {
      //读取Buffer数组格式的文件数据
      reader.readAsArrayBuffer(file);
    }

    return new Promise(function (resolve, reject) {
      //文件读取成功的回调
      reader.onload = function () {
        let result = this.result;
        resolve(result);
      };
      //文件读取中断的回调
      reader.onabort = function (e) {
        reject(e);
      };
      //文件读取错误的回调
      reader.onerror = function (e) {
        reject(e);
      };
    });
  },
  /**
   * 获取Base64数据：根据图片地址
   * 参考：https://blog.csdn.net/Jensen_Yao/article/details/83312046
   * @param url 图片地址
   * @param config 配置信息
   */
  getBase64FromUrl(url, config = {}) {
    let tempImage = new Image();
    tempImage.crossOrigin = '*';
    return new Promise((resolve, reject) => {
      tempImage.onload = () => {
        resolve(this.getBase64FromImg(tempImage, config));
      };
      tempImage.onerror = (e) => {
        reject(e);
      };
      tempImage.src = url;
    });
  },
  /**
   * 获取Base64数据：根据图片组件
   * 注意：通过canvas实现图片缩放，效果不好，不建议使用
   * 参考：https://blog.csdn.net/Jensen_Yao/article/details/83312046
   * @param img 图片组件：<img/>
   * @param config 配置信息
   *    imgType 图片类型。默认值：image/jpeg。
   *    scale 图片宽高压缩比例。默认值：1。
   *    quality 图片质量系数。默认值：0.75。
   */
  getBase64FromImg(img, config = {}) {
    const {imgType = MIMETypes.jpg, scale = 1, quality = 0.75} = config;
    let width = img.width * scale;
    let height = img.height * scale;

    let canvas = document.createElement('canvas');
    let ctx = canvas.getContext('2d');
    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(img, 0, 0, width, height);

    /**
     * canvas.toDataURL(mimeType, quality):把画布转换成base64格式信息图像信息
     *  mimeType：canvas导出的base64图片类型。默认值：'image/png'。
     *  quality：canvas导出的图片质量，只有导出为jpg和webp格式的时候此参数才有效果。默认值：0.92。
     */
    let dataURL = canvas.toDataURL(imgType, quality);
    return dataURL;
  },
  /**
   * 获取二进制数据：根据Base64数据
   * 参考：https://blog.csdn.net/Jensen_Yao/article/details/83312046
   * @param baseUrl Base64数据
   * @param fileName 文件名
   */
  getBlobFormBase64(baseUrl, fileName) {
    let arr = baseUrl.split(',');
    let mime = arr[0].match(/:(.*?);/)[1];
    let bstr = atob(arr[1]);
    let n = bstr.length;
    let u8arr = new Uint8Array(n);
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }
    if (fileName) {
      return new File([u8arr], fileName, {type: mime});
    } else {
      return new Blob([u8arr], {type: mime});
    }
  },
  /**
   * 压缩图片
   * @param baseUrl Base64的图片地址
   * @param maxSize 图片的最大值
   */
  compressImg(baseUrl, maxSize) {
    let scale = 1; //图片宽高压缩比例
    let quality = 0.75; //图片质量系数

    //获取图片类型
    let arr = baseUrl.split(',');
    let imgType = arr[0].match(/:(.*?);/)[1];

    let tempImage = new Image();
    tempImage.crossOrigin = '*';
    return new Promise((resolve, reject) => {
      tempImage.onload = () => {
        let result = baseUrl;
        let sizeNow = this.getBlobFormBase64(result).size; //当前图片大小
        //先压缩图片质量，再压缩图片宽高
        while (sizeNow > maxSize) {
          if (imgType === MIMETypes.jpg && quality > 0.1) {
            quality = quality / 2;
          } else {
            scale = scale / 2;
          }
          result = this.getBase64FromImg(tempImage, {imgType, scale, quality}); //压缩后的图片地址
          sizeNow = this.getBlobFormBase64(result).size; //当前图片大小
        }
        resolve(result);
      };
      tempImage.onerror = (e) => {
        reject(e);
      };
      tempImage.src = baseUrl;
    });
  },
  /**
   * 下载
   * 注意：下载xls、xlsx等文件时，需要设置axios的responseType: 'blob'。
   * @param result 请求结果。类型：{data,headers}。
   * @param config 文件配置。类型：{type}。
   */
  download(result = {}, config = {}) {
    const {type} = config;
    let {data, headers} = result;
    //使用微软的表格打开scv文件时，需要加上'\ufeff'前缀。
    if (type === 'csv') {
      data = '\ufeff' + data;
    }
    let name = headers['content-disposition'].split(';')[1].split('filename=')[1]; //从headers从获取文件名
    this.downloadFile(decodeURIComponent(name), data, MIMETypes[type]);
  },
  /**
   * 文件下载
   * 参考：https://developer.mozilla.org/zh-CN/docs/Web/API/Blob
   * @param name 文件名称。类型：String。如果文件名称中没有文件后缀，则根据文件类型自动生成。
   * @param data 文件内容。类型：Object。
   * @param type 文件类型。类型：String。非必填。
   */
  downloadFile(name, data, type) {
    const blob = new Blob([data], {type});
    const a = document.createElement('a'); //创建链接下载文件：用于非IE浏览器
    if ('download' in document.createElement('a')) {
      a.download = name;
      a.style.display = 'none';
      a.href = URL.createObjectURL(blob);
      document.body.appendChild(a);
      a.click();
      URL.revokeObjectURL(a.href);
      document.body.removeChild(a);
    } else {
      navigator.msSaveBlob(blob, name); // IE浏览器下载文件
    }
  },

  /********************************存储数据********************************/

  /**
   * 获取数据:以键值对的形式保存在本地
   * @param name 键
   * @param data 值
   * @param type 2种类型:0=sessionStorage,1=localStorage;
   */
  getItem(name, type = 0) {
    let storage = type === 0 ? sessionStorage : localStorage;
    //注意：IE9不支持本地网页的sessionStorage和localStorage，只支持服务器网页的
    if (!storage) return;
    let result = storage.getItem(name);
    result = JSON.parse(result);
    return result;
  },
  /**
   * 保存数据:以键值对的形式保存在本地。支持直接保存对象。
   * @param name 键
   * @param data 值
   * @param type 2种类型:0=sessionStorage,1=localStorage;
   */
  setItem(name, data, type = 0) {
    let storage = type === 0 ? sessionStorage : localStorage;
    data = JSON.stringify(data);
    return storage.setItem(name, data);
  },

  /**
   * 获取用户信息
   * @param type 2种类型:0=sessionStorage,1=localStorage;
   */
  getUser(type = 1) {
    let data = this.getItem(keys.user, type);
    return data || {};
  },

  /**
   * 保存用户信息
   * @param data 用户信息
   * @param type 2种类型:0=sessionStorage,1=localStorage;
   */
  setUser(data, type = 1) {
    if (!data) return;
    this.setItem(keys.user, data, type);
  },

  /********************************计算css********************************/

  /**
   * 将375px屏宽的像素值转为百分比的屏宽
   * @param data 375px屏宽的像素值
   */
  px375(data) {
    let result = (data / 375) * window.screen.width;
    if (result < 1) {
      result = 1;
    }
    return result;
  }
};
